package utils;

import mathematics.geometry.Geometry;

public class Point2D {

	public double [] p = new double[2];

	public Point2D() {
		p[0] = p[1] = 0;
	}

	public Point2D( double x, double y ) {
		p[0] = x;
		p[1] = y;
	}

	public Point2D( Point2D P ) {
		p[0] = P.p[0];
		p[1] = P.p[1];
	}
	
	public Point2D( Point3D P ) {
		p[0] = P.p[0];
		p[1] = P.p[1];
	}

	public Point2D( Vector2D V ) {
		p[0] = V.v[0];
		p[1] = V.v[1];
	}

	public void copy( double x, double y ) {
		p[0] = x;
		p[1] = y;
	}

	public void copy( Point2D P ) {
		p[0] = P.p[0];
		p[1] = P.p[1];
	}

	public boolean equals( Point2D other ) {
		return x() == other.x() && y() == other.y();
	}

	public double x() { return p[0]; }
	public double y() { return p[1]; }

	// used to pass coordinates directly to OpenGL routines
	public double [] get() { return p; }

	// return the difference between two given points
	static public Vector2D diff( Point2D a, Point2D b ) {
		return new Vector2D( a.x()-b.x(), a.y()-b.y() );
	}

	// return the sum of the given point and vector
	static public Point2D sum( Point2D a, Vector2D b ) {
		return new Point2D( a.x()+b.x(), a.y()+b.y() );
	}

	// return the difference between the given point and vector
	static public Point2D diff( Point2D a, Vector2D b ) {
		return new Point2D( a.x()-b.x(), a.y()-b.y() );
	}

	public double distance( Point2D otherPoint ) {
		return diff( this, otherPoint ).length();
	}

	static public Point2D average( Point2D a, Point2D b ) {
		// return new Point2D( Vector2D.mult( Vector2D.sum( new Vector2D(a), new Vector2D(b) ), 0.5f ) );
		return new Point2D( (a.x()+b.x())*0.5f, (a.y()+b.y())*0.5f );
	}

	public String toString() {
		return "[" + p[0] + ","+ p[1] + "]";
	}

	public java.awt.geom.Point2D.Double toPoint() {
		return new java.awt.geom.Point2D.Double(x(), y());
	}
	

    public static Vector2D normal(Point2D A, Point2D B, Point2D C, float thickness)
    {
     	Vector2D v1 = new Vector2D(A,B);
    	Vector2D v2 = new Vector2D(B,C);
    	
    	Vector2D perpendV1 = new Vector2D(-v1.y(), v1.x());
    	perpendV1 = perpendV1.normalized();
    	perpendV1 = Vector2D.mult(perpendV1, thickness);
    	
    	Vector2D perpendV2 = new Vector2D(-v2.y(), v2.x());
    	perpendV2 = perpendV2.normalized();
    	perpendV2 = Vector2D.mult(perpendV2, thickness);
    	
    	
    	Point2D perpA = Point2D.sum(A, perpendV1);
    	Point2D perpB1 = Point2D.sum(B, perpendV1);
    	Point2D perpB2 = Point2D.sum(B, perpendV2);
    	Point2D perpC = Point2D.sum(C, perpendV2);
    	
    	Point2D result = Point2D.intersectionPoint(perpA, perpB1, perpC, perpB2);
    	
    	return Point2D.diff(result, B);
    }
    
    public static boolean normalIsInside(Point2D A, Point2D B, Point2D C, float thickness)
    {
     	Vector2D v1 = new Vector2D(A,B);
    	Vector2D v2 = new Vector2D(B,C);
    	
    	Vector2D perpendV1 = new Vector2D(-v1.y(), v1.x());
    	perpendV1 = perpendV1.normalized();
    	perpendV1 = Vector2D.mult(perpendV1, thickness);
    	
    	Vector2D perpendV2 = new Vector2D(-v2.y(), v2.x());
    	perpendV2 = perpendV2.normalized();
    	perpendV2 = Vector2D.mult(perpendV2, thickness);
    	
    	
    	Point2D perpA = Point2D.sum(A, perpendV1);
    	Point2D perpB1 = Point2D.sum(B, perpendV1);
    	Point2D perpB2 = Point2D.sum(B, perpendV2);
    	Point2D perpC = Point2D.sum(C, perpendV2);
    	
    	return Point2D.intersectionPointIsInside(perpA, perpB1, perpC, perpB2);
    	
    }
    
    public static Point2D intersectionPoint(Point2D p1, Point2D p2, Point2D p3, Point2D p4)
    {	
         
    	double eps = 0.0000000001;
    	double denom = (p4.y() - p3.y()) * (p2.x() - p1.x()) - (p4.x() - p3.x()) * ( p2.y() - p1.y());
    	double numera = (p4.x() - p3.x()) * (p1.y() - p3.y()) - (p4.y() - p3.y()) * (p1.x() - p3.x());
    	double numerb = (p2.x() - p1.x()) * (p1.y() - p3.y()) - (p2.y() - p1.y()) * (p1.x() - p3.x());
        
       	
    	if( (- eps < numera && numera < eps) &&
    		(- eps < numerb && numerb < eps )	&&
    		(- eps < denom && denom < eps ) )
    	{
    		double x = (p1.x() + p2.x()) * 0.5;
    		double y = (p1.y() + p2.y()) * 0.5;
    		return new Point2D(x, y);
    	}
    	
    	
    	if( (- Double.MIN_VALUE < denom && denom < Double.MIN_VALUE ) )
    	{
    		return p2;
    	}
    	
    	double mua = numera / denom;
    	
    	double x = p1.x() + mua * (p2.x() - p1.x());
    	double y = p1.y() + mua * (p2.y() - p1.y());
    	
    	return new Point2D(x, y);
    }

    public static boolean intersectionPointIsInside(Point2D p1, Point2D p2, Point2D p3, Point2D p4)
    {	
         
    	double eps = 0.0000000001;
    	double denom = (p4.y() - p3.y()) * (p2.x() - p1.x()) - (p4.x() - p3.x()) * ( p2.y() - p1.y());
    	double numera = (p4.x() - p3.x()) * (p1.y() - p3.y()) - (p4.y() - p3.y()) * (p1.x() - p3.x());
    	double numerb = (p2.x() - p1.x()) * (p1.y() - p3.y()) - (p2.y() - p1.y()) * (p1.x() - p3.x());
        
       	
    	if( (- eps < numera && numera < eps) &&
    		(- eps < numerb && numerb < eps )	&&
    		(- eps < denom && denom < eps ) )
    	{
    		return false;
    	}
    	
    	
    	if( (- Double.MIN_VALUE < denom && denom < Double.MIN_VALUE ) )
    	{
    		return false;
    	}
    	
    	double mua = numera / denom;
    	double mub = numerb / denom;
    	
    	
    	boolean out1 = mua < 0 || mua > 1;
    	boolean out2 = mub < 0 || mub > 1;

    	if ( out1 & out2) 
    	{
    		return false;
    	} else 
    	{
    		return true;
    	}
    }

	public boolean isInside(Geometry shape) {
		// This code was copied, with minor changes, from
		//    http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
		// where it (the code, not the algorithm) is attributed to Randolph Franklin.
		// The idea behind the algorithm is to imagine a ray projecting
		// from the point toward the right, and then count how many times
		// that ray intersects an edge of the polygon.
		// If the number is odd, the point is inside the polygon.

		boolean returnValue = false;
		int i, j;

		for (i = 0, j = shape.size()-1; i < shape.size(); j = i++) {

			Point3D pi = shape.get(i);
			double xi = pi.x();
			double yi = pi.y();
			Point3D pj = shape.get(j);
			double xj = pj.x();
			double yj = pj.y();

			if (
				(((yi <= this.y()) && (this.y() < yj)) || ((yj <= this.y()) && (this.y() < yi)))
				&& (this.x() < (xj - xi) * (this.y() - yi) / (yj - yi) + xi)
			) {
				returnValue = ! returnValue;
			}
		}
		return returnValue;
	}

	public static Point2D sum(Point2D position, Point2D position2) {
		// TODO Auto-generated method stub
		return new Point2D(position.x() + position2.x(), position.y() + position2.y());
	}

}

